/**
  **************************************************************************
  * @file     xmc_ecc.c
  * @version  v2.0.3
  * @date     2021-12-17
  * @brief    nand ecc configuration
  **************************************************************************
  *                       Copyright notice & Disclaimer
  *
  * The software Board Support Package (BSP) that is made available to 
  * download from Artery official website is the copyrighted work of Artery. 
  * Artery authorizes customers to use, copy, and distribute the BSP 
  * software and its related documentation for the purpose of design and 
  * development in conjunction with Artery microcontrollers. Use of the 
  * software is governed by this copyright notice and the following disclaimer.
  *
  * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
  * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
  * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
  * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
  * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
  *
  **************************************************************************
  */

#include "bsp.h"
#include "bsp_xmc_nand.h"
#include "framework.h"

/** @addtogroup 437_XMC_nand_ecc
  * @{
  */
#define ECC_DATA_SIZE 512
//#define ECC_DATA_SIZE 1024

uint32_t ecc_value_write = 0, ecc_value_read = 0, ecc_value_write_last = 0;
nand_attriute atio_nand;

/**
  * @brief  configures the xmc and gpios to interface with the nand memory.
  *         this function must be called before any write/read operation on the 
  *         nand.
  * @param  none
  * @retval none
  */
void nand_init(void)
{
	gpio_init_type gpio_init_struct;
	xmc_nand_init_type nand_init_struct;
	xmc_nand_pccard_timinginit_type xmc_regular_spacetimingstruct;

	/* enable the xmc clock */
	crm_periph_clock_enable(CRM_XMC_PERIPH_CLOCK, TRUE);
	/* enable gpiod/gpiob/gpioe clock */
	crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);
	crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, TRUE);

	/*-- gpio configuration ------------------------------------------------------*/
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE0, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE1, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE11, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE12, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE14, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE15, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE4, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE5, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE6, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE7, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOE, GPIO_PINS_SOURCE7, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOE, GPIO_PINS_SOURCE8, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOE, GPIO_PINS_SOURCE9, GPIO_MUX_12);
	gpio_pin_mux_config(GPIOE, GPIO_PINS_SOURCE10, GPIO_MUX_12);

	/* cle, ale, d0->d3, noe, nwe , nce2 and nwait nand pin configuration  */
	gpio_default_para_init(&gpio_init_struct);
	gpio_init_struct.gpio_pins = GPIO_PINS_11 | GPIO_PINS_12 | GPIO_PINS_14 | GPIO_PINS_15 |
								 GPIO_PINS_0 | GPIO_PINS_1 | GPIO_PINS_4 | GPIO_PINS_5 |
								 GPIO_PINS_6 | GPIO_PINS_7;
	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
	gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
	gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
	gpio_init(GPIOD, &gpio_init_struct);

	/* d4->d7 nand pin configuration  */
	gpio_init_struct.gpio_pins = GPIO_PINS_7 | GPIO_PINS_8 | GPIO_PINS_9 | GPIO_PINS_10;
	gpio_init(GPIOE, &gpio_init_struct);

	/*-- xmc configuration ------------------------------------------------------*/
	xmc_nand_default_para_init(&nand_init_struct);
	nand_init_struct.nand_bank = XMC_BANK2_NAND;
	nand_init_struct.wait_enable = XMC_WAIT_OPERATION_DISABLE;
	nand_init_struct.bus_type = XMC_BUSTYPE_8_BITS;
	nand_init_struct.ecc_enable = XMC_ECC_OPERATION_ENABLE;
#if ECC_DATA_SIZE == 512
	nand_init_struct.ecc_pagesize = XMC_ECC_PAGESIZE_512_BYTES;
#else
	nand_init_struct.ecc_pagesize = XMC_ECC_PAGESIZE_1024_BYTES;
#endif
	nand_init_struct.delay_time_cycle = 0x10;
	nand_init_struct.delay_time_ar = 0x10;
	xmc_nand_init(&nand_init_struct);

	xmc_nand_timing_default_para_init(&xmc_regular_spacetimingstruct, &xmc_regular_spacetimingstruct);
	xmc_regular_spacetimingstruct.class_bank = XMC_BANK2_NAND;
	xmc_regular_spacetimingstruct.mem_setup_time = 2 + 2;
	xmc_regular_spacetimingstruct.mem_hiz_time = 1 + 2;
	xmc_regular_spacetimingstruct.mem_hold_time = 2 + 2;
	xmc_regular_spacetimingstruct.mem_waite_time = 3 + 2;
	xmc_nand_timing_config(&xmc_regular_spacetimingstruct, &xmc_regular_spacetimingstruct);

	/* xmc nand bank cmd test */
	xmc_nand_enable(XMC_BANK2_NAND, TRUE);

	atio_nand.block_totalnum = 1024;
	atio_nand.block_pagenum = 64;
	atio_nand.plane_blocknum = 1024;
	atio_nand.page_mainsize = 2048;
	atio_nand.page_sparesize = 64;
	atio_nand.page_totalsize = 2048 + 64;

	xmc_nand_ecc_enable(XMC_BANK2_NAND, FALSE);
	nand_reset();
}

/**
  * @brief  reads nand memory's id.
  * @param  nand_id: pointer to a nand_id_type structure which will hold
  *         the manufacturer and device id.  
  * @retval none
  */
uint32_t nand_read_id(nand_id_type *nand_id_struct)
{
	uint32_t data = 0, addr;

	/* send command to the command area */
	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_READID;
	*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;

	addr = Bank_NAND_ADDR | DATA_AREA;

	/* sequence to read id from nand flash */
	nand_id_struct->maker_id = *(__IO uint8_t *)(addr);
	nand_id_struct->device_id = *(__IO uint8_t *)(addr);
	nand_id_struct->third_id = *(__IO uint8_t *)(addr);
	nand_id_struct->fourth_id = *(__IO uint8_t *)(addr);
	nand_id_struct->fifth_id = *(__IO uint8_t *)(addr);
	nand_id_struct->sixth_id = *(__IO uint8_t *)(addr);

	atio_nand.id = nand_id_struct->id;

	return atio_nand.id;
}

/**
  * @brief  this routine is for writing one or several 512 bytes page size.
  * @param  pbuffer: pointer on the buffer containing data to be written 
  * @param  address: first page address
  * @param  num_page_to_write: number of page to write  
  * @retval new status of the nand operation. this parameter can be:
  *         - NAND_TIMEOUT_ERROR: when the previous operation generate 
  *           a timeout error
  *         - NAND_READY: when memory is ready for the next operation 
  *           and the new status of the increment address operation. it can be:
  *         - NAND_VALID_ADDRESS: when the new address is valid address
  *         - NAND_INVALID_ADDRESS: when the new address is invalid address  
  */
uint32_t nand_write_page(uint8_t *pbuffer, uint32_t page, uint32_t coil, uint16_t len)
{
	uint32_t index = 0x00, num_page_written = 0x00, ecc_size;
	uint32_t status = NAND_READY;
	uu8 *p_wr, wr_cnt;

	/* page write command and address */
	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_AREA_A;
	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_WRITE0;

	if (atio_nand.block_totalnum <= 1024)
	{
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(ROW_ADDRESS);
	}
	else
	{
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_3rd_cycle(ROW_ADDRESS);
	}

	/* write data */
	p_wr = pbuffer;
	if (coil || len)
	{
		/* write spare */
		for (index = 0; index < len; index++)
		{
			*(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA) = *p_wr++;
		}
	}
	else
	{
		for (wr_cnt = 0; wr_cnt < atio_nand.page_mainsize / ECC_DATA_SIZE; wr_cnt++)
		{
			xmc_nand_ecc_enable(XMC_BANK2_NAND, TRUE);
			for (index = 0x00; index < ECC_DATA_SIZE; index++)
			{
				*(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA) = *p_wr++;
			}

			while (!XMC_BANK2->bk2is_bit.fifoe) /* wait fifo empty */
				;
			atio_nand.ecc_hdbuf[wr_cnt] = xmc_ecc_get(XMC_BANK2_NAND);
			xmc_nand_ecc_enable(XMC_BANK2_NAND, FALSE);
		}

		/* write spare */
		for (index = 0; index < 16; index++)
		{
			*(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA) = *p_wr++;
		}

		/* write ecc */
		p_wr = (uu8 *)atio_nand.ecc_hdbuf;
		ecc_size = 4 * atio_nand.page_mainsize / ECC_DATA_SIZE;
		for (index = 0; index < ecc_size; index++)
		{
			*(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA) = p_wr[index];
		}
	}

	/* program exe */
	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_WRITE_TRUE1;

	while (xmc_flag_status_get(XMC_BANK2_NAND, XMC_FEMPT_FLAG) == RESET)
		;
	/* check status for successful operation */
	return nand_get_status();
}

/**
  * @brief  this routine is for sequential read from one or several 512 bytes page size.
  * @param  pbuffer: pointer on the buffer to fill
  * @param  address: first page address
  * @param  num_page_to_read: number of page to read  
  * @retval new status of the nand operation. this parameter can be:
  *         - NAND_TIMEOUT_ERROR: when the previous operation generate 
  *           a timeout error
  *         - NAND_READY: when memory is ready for the next operation 
  *           and the new status of the increment address operation. it can be:
  *         - NAND_VALID_ADDRESS: when the new address is valid address
  *         - NAND_INVALID_ADDRESS: when the new address is invalid address
  */
uint32_t nand_read_page(uint8_t *pbuffer, uint32_t page, uint32_t coil, uint16_t len)
{
	uint32_t index = 0x00, num_page_read = 0x00;
	uint32_t status = NAND_READY, i, ecc_size;
	uint8_t rd_cnt, *p_rd;

	/* page read command and page address */
	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_AREA_A;

	if (atio_nand.block_totalnum <= 1024)
	{
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(ROW_ADDRESS);
	}
	else
	{
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(COL_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_3rd_cycle(ROW_ADDRESS);
	}

	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_AREA_TRUE1;

	bsp_delay_us(200);

	atio_nand.ecc_err_st = 0;
	atio_nand.ecc_cok_st = 0;
	/* get data into buffer */
	p_rd = pbuffer;
	if (coil || len)
	{
		/* get spare */
		for (index = 0x00; index < len; index++)
		{
			*p_rd++ = *(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA);
		}
	}
	else
	{

		for (rd_cnt = 0; rd_cnt < atio_nand.page_mainsize / ECC_DATA_SIZE; rd_cnt++)
		{
			xmc_nand_ecc_enable(XMC_BANK2_NAND, TRUE);

			for (index = 0x00; index < ECC_DATA_SIZE; index++)
			{
				*p_rd++ = *(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA);
			}
			while (!XMC_BANK2->bk2is_bit.fifoe) /* wait fifo empty */
				;
			atio_nand.ecc_hdbuf[rd_cnt] = xmc_ecc_get(XMC_BANK2_NAND);
			xmc_nand_ecc_enable(XMC_BANK2_NAND, FALSE);
		}

		/* get spare */
		for (index = 0x00; index < 16; index++)
		{
			*p_rd++ = *(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA);
		}
		/* get ecc */
		p_rd = (uu8 *)atio_nand.ecc_rdbuf;
		ecc_size = 4 * atio_nand.page_mainsize / ECC_DATA_SIZE;
		for (index = 0x00; index < ecc_size; index++)
		{
			*p_rd++ = *(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA);
		}
		/* ecc check */
		for (rd_cnt = 0; rd_cnt < atio_nand.page_mainsize / ECC_DATA_SIZE; rd_cnt++)
		{
			if (atio_nand.ecc_rdbuf[rd_cnt] != atio_nand.ecc_hdbuf[rd_cnt])
			{
				uint8_t ecc_err;
				ecc_err = nand_ecc_correction(
					&pbuffer[rd_cnt * ECC_DATA_SIZE],
					atio_nand.ecc_rdbuf[rd_cnt],
					atio_nand.ecc_hdbuf[rd_cnt]);

				if (ecc_err)
				{
					atio_nand.ecc_err_st++;
					dbg_print("ECC CORRECT FAIL @ PAGE %d, SECTOR %d\n", page, rd_cnt);
				}
				else
				{
					atio_nand.ecc_cok_st++;
					dbg_print("ECC CORRECT OK @ PAGE %d, SECTOR %d\n", page, rd_cnt);
				}
			}
		}
	}
	/* calculate page address */
	return nand_get_status();
}

/**
  * @brief  this routine write the spare area information for the specified
  *         pages addresses.  
  * @param  pbuffer: pointer on the buffer containing data to be written 
  * @param  address: first page address
  * @param  num_spare_area_to_write: number of spare area to write
  * @retval new status of the nand operation. this parameter can be:
  *         - NAND_TIMEOUT_ERROR: when the previous operation generate 
  *           a timeout error
  *         - NAND_READY: when memory is ready for the next operation 
  *           and the new status of the increment address operation. it can be:
  *         - NAND_VALID_ADDRESS: when the new address is valid address
  *         - NAND_INVALID_ADDRESS: when the new address is invalid address
  */
uint32_t nand_write_spare_area(uint8_t *pbuffer, uint32_t page, uint32_t coil, uint16_t len)
{
	return nand_write_page(pbuffer, page, coil + atio_nand.page_mainsize, len);
}

/**
  * @brief  this routine read the spare area information from the specified
  *         pages addresses.  
  * @param  pbuffer: pointer on the buffer to fill 
  * @param  address: first page address
  * @param  num_spare_area_to_read: number of spare area to read
  * @retval new status of the nand operation. this parameter can be:
  *         - NAND_TIMEOUT_ERROR: when the previous operation generate 
  *           a timeout error
  *         - NAND_READY: when memory is ready for the next operation 
  *           and the new status of the increment address operation. it can be:
  *         - NAND_VALID_ADDRESS: when the new address is valid address
  *         - NAND_INVALID_ADDRESS: when the new address is invalid address
  */
uint32_t nand_read_spare_area(uint8_t *pbuffer, uint32_t page, uint32_t coil, uint16_t len)
{
	return nand_read_page(pbuffer, page, coil + atio_nand.page_mainsize, len);
}

/**
  * @brief  this routine erase complete block from nand flash
  * @param  address: any address into block to be erased
  * @retval new status of the nand operation. this parameter can be:
  *         - NAND_TIMEOUT_ERROR: when the previous operation generate 
  *           a timeout error
  *         - NAND_READY: when memory is ready for the next operation 
  */
uint32_t nand_erase_block(uint16_t block)
{
	uint32_t page = block * atio_nand.block_pagenum;

	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_ERASE0;

	if (atio_nand.block_totalnum <= 1024)
	{
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(ROW_ADDRESS);
	}
	else
	{
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_1st_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_2nd_cycle(ROW_ADDRESS);
		*(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = addr_3rd_cycle(ROW_ADDRESS);
	}

	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_ERASE1;

	return (nand_get_status());
}

/**
  * @brief  this routine reset the nand flash.
  * @param  none
  * @retval NAND_READY
  */
uint32_t nand_reset(void)
{
	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_RESET;

	bsp_delay_us(50);

	nand_get_status();

	return (NAND_READY);
}

/**
  * @brief  get the nand operation status.
  * @param  none
  * @retval new status of the nand operation. this parameter can be:
  *         - NAND_TIMEOUT_ERROR: when the previous operation generate
  *           a timeout error
  *         - NAND_READY: when memory is ready for the next operation
  */
uint32_t nand_get_status(void)
{
	uint32_t timeout = 0x1000000, status = NAND_READY;

	status = nand_read_status();

	/* wait for a nand operation to complete or a timeout to occur */
	while ((status != NAND_READY) && (timeout != 0x00))
	{
		status = nand_read_status();
		timeout--;
	}

	if (timeout == 0x00)
	{
		status = NAND_TIMEOUT_ERROR;
	}

	/* return the operation status */
	return (status);
}

/**
  * @brief  reads the nand memory status using the read status command. 
  * @param  none
  * @retval the status of the nand memory. this parameter can be:
  *         - NAND_BUSY: when memory is busy
  *         - NAND_READY: when memory is ready for the next operation
  *         - NAND_ERROR: when the previous operation gererates error
  */
uint32_t nand_read_status(void)
{
	uint32_t data = 0x00, status = NAND_BUSY;

	/* read status operation */
	*(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_STATUS;
	data = *(__IO uint8_t *)(Bank_NAND_ADDR);

	if ((data & NAND_ERROR) == NAND_ERROR)
	{
		status = NAND_ERROR;
	}
	else if ((data & NAND_READY) == NAND_READY)
	{
		status = NAND_READY;
	}
	else
	{
		status = NAND_BUSY;
	}

	return (status);
}

/**
  * @brief  ecc correct 1 bit error, 该算法验证失败
  * @param  pbuffer: pointer on the buffer to correct 
  * @param  tx_ecc_value: ecc value for transmiting to nand flash
  * @param  rx_ecc_value: ecc value for receiving from nand flash
  */
uint8_t nand_ecc_correction_(uint8_t *pbuffer, uint32_t tx_ecc_value, uint32_t rx_ecc_value)
{
	uint32_t ecc_value, position, byte_position;
	uint8_t i, compare_data;
	uint8_t bits;

	/* check ecc value */
	if (tx_ecc_value != rx_ecc_value)
	{
		/* ECC 8192 byte -- 32bit ecc valid value */
		/* ECC 2048 byte -- 28bit ecc valid value */
		/* ECC 1024 byte -- 26bit ecc valid value */
		/* ECC 512 byte -- 24bit ecc valid value */

#if ECC_DATA_SIZE == 512
		ecc_value = (tx_ecc_value ^ rx_ecc_value) & 0x00FFFFFF;
		bits = 24 / 2;
#elif ECC_DATA_SIZE == 1024
		ecc_value = (tx_ecc_value ^ rx_ecc_value) & 0x03FFFFFF;
		bits = 26 / 2;
#else
#error "ECC DAT SIZE ERROR !"
#endif
		for (i = 0; i < bits; i++)
		{
			compare_data = (ecc_value >> (i * 2)) & 0x3;

			/* find position */
			if (compare_data == 0x2)
			{
				position |= (1 << i);
			}

			/* more than 1 bit erroc */
			else if (compare_data != 0x1)
			{
				return 1;
			}
		}

		/* correct receive value */
		byte_position = position / 8;
		pbuffer[byte_position] ^= 1 << (position % 8);

		dbg_print("error pos: bit %d.%d \n", position >> 3, position & 7);
	}

	return 0;
}

/*****************************************************************************
* @brief   atk_nand_get_OE. 获取ECC的奇数位/偶数位
* @param   oe :0,偶数位, 1,奇数位
* @param   eccval:输入的ecc值
* @return  计算后的ecc值(最多16位)
*****************************************************************************/
#if ECC_DATA_SIZE == 512
#define ECC_S_BITS 24
#define ECC_S_MASK 0x0FFF
#elif ECC_DATA_SIZE == 1024
#define ECC_S_BITS 26
#define ECC_S_MASK 0x1FFF
#endif

u16 atk_nand_get_OE(u8 oe, u32 eccval)
{
	u8 i;
	u16 ecctemp = 0;

	for (i = 0; i < ECC_S_BITS; i++)
	{
		if ((i % 2) == oe)
		{
			if ((eccval >> i) & 0X01)
				ecctemp += 1 << (i >> 1);
		}
	}
	return ecctemp;
}

/*****************************************************************************
* @brief   atk_nand_ecc_correct. ECC校正函数,  512B AND 1024B ECC验证OK
* @param   eccrd:读取出来,原来保存的ECC值
* @param   ecccl:读取数据时,硬件计算的ECC
* @return  0,错误已修正, 其他,ECC错误(有大于2个bit的错误,无法恢复)
*****************************************************************************/
uint8_t nand_ecc_correction(uint8_t *data_buf, uint32_t eccrd, uint32_t ecccl)
//u8 atk_nand_ecc_correct(u8 *data_buf, u32 eccrd, u32 ecccl)
{
	u16 eccrdo, eccrde, eccclo, ecccle;
	u16 eccchk = 0;
	u16 errorpos = 0;
	u32 bytepos = 0;
	eccrdo = atk_nand_get_OE(1, eccrd); //获取eccrd的奇数位
	eccrde = atk_nand_get_OE(0, eccrd); //获取eccrd的偶数位
	eccclo = atk_nand_get_OE(1, ecccl); //获取ecccl的奇数位
	ecccle = atk_nand_get_OE(0, ecccl); //获取ecccl的偶数位
	eccchk = eccrdo ^ eccrde ^ eccclo ^ ecccle;
	if (eccchk == ECC_S_MASK) //全1,说明只有1bit ECC错误
	{
		errorpos = eccrdo ^ eccclo;
		bytepos = errorpos / 8;
		data_buf[bytepos] ^= 1 << (errorpos % 8);

		dbg_print("error pos: bit %d.%d \n", errorpos >> 3, errorpos & 7);
	}
	else //不是全1,说明至少有2bit ECC错误,无法修复
	{
		dbg_print("2bit ecc error or more \n");
		return 1;
	}
	return 0;
}

/**
  * @}
  */

/**
  * @}
  */
